using DeviceCommons.DeviceMessages.Enums;
using DeviceCommons.DeviceMessages.Models;

namespace DeviceCommons.Validation
{
    /// <summary>
    /// DeviceCommons 验证框架
    /// 提前检测常见异常情况，提供清晰的错误信息
    /// </summary>
    public static class DeviceMessageValidator
    {
        /// <summary>
        /// 验证设备ID
        /// </summary>
        /// <param name="deviceId">设备ID</param>
        /// <param name="paramName">参数名</param>
        /// <exception cref="ArgumentNullException">设备ID为null时抛出</exception>
        /// <exception cref="DeviceMessageValidationException">设备ID为空字符串或超过最大长度时抛出</exception>
        public static void ValidateDeviceId(string? deviceId, string paramName = "deviceId")
        {
            if (deviceId == null)
                throw new ArgumentNullException(paramName, "设备ID不能为null");
            
            if (string.IsNullOrEmpty(deviceId))
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidDeviceId,
                    "设备ID不能为空字符串",
                    paramName,
                    "非空字符串",
                    "空字符串");
            
            if (deviceId.Length > byte.MaxValue)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidDeviceId,
                    $"设备ID长度不能超过{byte.MaxValue}个字符",
                    paramName,
                    $"<= {byte.MaxValue} 字符",
                    $"{deviceId.Length} 字符");
        }

        /// <summary>
        /// 验证设备类型
        /// </summary>
        /// <param name="deviceType">设备类型</param>
        /// <exception cref="ArgumentOutOfRangeException">设备类型为0时抛出</exception>
        public static void ValidateDeviceType(byte deviceType)
        {
            if (deviceType == 0)
                throw new ArgumentOutOfRangeException(nameof(deviceType), "设备类型不能为0");
        }

        /// <summary>
        /// 验证状态ID
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <exception cref="ArgumentOutOfRangeException">状态ID为0时抛出</exception>
        public static void ValidateStateId(byte stateId)
        {
            if (stateId == 0)
                throw new ArgumentOutOfRangeException(nameof(stateId), "状态ID不能为0");
        }

        /// <summary>
        /// 验证状态值
        /// </summary>
        /// <param name="value">状态值</param>
        /// <param name="valueType">值类型</param>
        /// <exception cref="ArgumentNullException">值为null时抛出</exception>
        /// <exception cref="ArgumentException">值类型不匹配或超出限制时抛出</exception>
        public static void ValidateStateValue(object? value, StateValueTypeEnum valueType)
        {
            if (value == null)
                throw new ArgumentNullException(nameof(value), "状态值不能为null");

            switch (valueType)
            {
                case StateValueTypeEnum.String:
                    if (value is string stringValue)
                    {
                        if (string.IsNullOrEmpty(stringValue))
                            throw new DeviceMessageValidationException(
                                ValidationErrorType.InvalidStateValue,
                                "字符串类型的状态值不能为空字符串",
                                nameof(value),
                                "非空字符串",
                                "空字符串");
                        
                        var byteLength = System.Text.Encoding.UTF8.GetByteCount(stringValue);
                        if (byteLength > byte.MaxValue)
                            throw new DeviceMessageValidationException(
                                ValidationErrorType.StringLengthExceeded,
                                $"字符串状态值的UTF-8字节长度超出范围，期望 <= {byte.MaxValue}，实际：{byteLength}",
                                nameof(value),
                                $"<= {byte.MaxValue} 字节",
                                $"{byteLength} 字节");
                    }
                    else
                    {
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望String类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "String类型",
                            value.GetType().Name);
                    }
                    break;

                case StateValueTypeEnum.Binary:
                    if (value is byte[] binaryValue)
                    {
                        if (binaryValue.Length == 0)
                            throw new DeviceMessageValidationException(
                                ValidationErrorType.InvalidStateValue,
                                "二进制类型的状态值不能为空数组",
                                nameof(value),
                                "非空字节数组",
                                "空数组");
                        
                        if (binaryValue.Length > byte.MaxValue)
                            throw new DeviceMessageValidationException(
                                ValidationErrorType.BinaryDataLengthExceeded,
                                $"二进制状态值长度超出范围，期望 <= {byte.MaxValue} 字节，实际：{binaryValue.Length} 字节",
                                nameof(value),
                                $"<= {byte.MaxValue} 字节",
                                $"{binaryValue.Length} 字节");
                    }
                    else
                    {
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望byte[]类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "byte[]类型",
                            value.GetType().Name);
                    }
                    break;

                case StateValueTypeEnum.Int32:
                    if (!(value is int))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望Int32类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "Int32类型",
                            value.GetType().Name);
                    break;

                case StateValueTypeEnum.Int16:
                    if (!(value is short))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望Int16类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "Int16类型",
                            value.GetType().Name);
                    break;

                case StateValueTypeEnum.UInt16:
                    if (!(value is ushort))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望UInt16类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "UInt16类型",
                            value.GetType().Name);
                    break;

                case StateValueTypeEnum.Float32:
                    if (!(value is float floatValue))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望Float32类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "Float32类型",
                            value.GetType().Name);
                    
                    if (float.IsNaN(floatValue) || float.IsInfinity(floatValue))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.InvalidFloatValue,
                            "Float32状态值不能为NaN或无穷大",
                            nameof(value),
                            "有效的数值",
                            floatValue);
                    break;

                case StateValueTypeEnum.Double:
                    if (!(value is double doubleValue))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望Double类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "Double类型",
                            value.GetType().Name);
                    
                    if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.InvalidFloatValue,
                            "Double状态值不能为NaN或无穷大",
                            nameof(value),
                            "有效的数值",
                            doubleValue);
                    break;

                case StateValueTypeEnum.Bool:
                    if (!(value is bool))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望Bool类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "Bool类型",
                            value.GetType().Name);
                    break;

                case StateValueTypeEnum.Timestamp:
                    if (!(value is ulong))
                        throw new DeviceMessageValidationException(
                            ValidationErrorType.StateValueTypeMismatch,
                            $"状态值类型不匹配，期望UInt64 (timestamp)类型，实际获得{value.GetType().Name}类型",
                            nameof(value),
                            "UInt64 (timestamp)类型",
                            value.GetType().Name);
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(valueType), valueType, $"不支持的状态值类型：{valueType}");
            }
        }

        /// <summary>
        /// 验证消息数据长度
        /// </summary>
        /// <param name="data">消息数据</param>
        /// <param name="paramName">参数名</param>
        /// <exception cref="ArgumentNullException">数据为null时抛出</exception>
        /// <exception cref="ArgumentException">数据长度不足时抛出</exception>
        public static void ValidateMessageData(byte[]? data, string paramName = "data")
        {
            if (data == null)
                throw new ArgumentNullException(paramName, "消息数据不能为null");
            
            if (data.Length == 0)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InsufficientDataLength,
                    "消息数据不能为空，至少需要4字节的消息头",
                    paramName,
                    "至少 4 字节",
                    "0 字节");
            
            if (data.Length < 4)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InsufficientDataLength,
                    $"消息数据长度不足，至少需要4字节的消息头，当前长度：{data.Length}字节",
                    paramName,
                    "4 字节",
                    $"{data.Length} 字节");
        }

        /// <summary>
        /// 验证十六进制字符串
        /// </summary>
        /// <param name="hexString">十六进制字符串</param>
        /// <param name="paramName">参数名</param>
        /// <exception cref="ArgumentNullException">字符串为null时抛出</exception>
        /// <exception cref="ArgumentException">字符串格式无效时抛出</exception>
        public static void ValidateHexString(string? hexString, string paramName = "hexString")
        {
            if (hexString == null)
                throw new ArgumentNullException(paramName, "十六进制字符串不能为null");
            
            if (string.IsNullOrEmpty(hexString))
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidHexFormat,
                    "十六进制字符串不能为空",
                    paramName,
                    "非空十六进制字符串",
                    "空字符串");

            // 检查是否包含前缀
            string hexPart = hexString;
            bool isEncrypted = false;
            bool isCompressed = false;
            
            if (hexString.Contains('|'))
            {
                var parts = hexString.Split('|', 2);
                if (parts.Length != 2)
                    throw new DeviceMessageValidationException(
                        ValidationErrorType.InvalidHexFormat,
                        $"十六进制字符串格式错误，应为 'prefix|hexdata' 格式：{hexString}",
                        paramName,
                        "'prefix|hexdata' 格式",
                        hexString);
                
                // 解析前缀
                var prefixParts = parts[0].Split(',');
                if (prefixParts.Length >= 2)
                {
                    isEncrypted = prefixParts[0].ToLower().Equals("enc");
                    isCompressed = prefixParts[1].ToLower().Equals("gzip");
                }
                
                hexPart = parts[1];
            }

            if (hexPart.Length == 0)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidHexFormat,
                    "数据部分不能为空",
                    paramName,
                    "非空数据部分",
                    "空数据");
            
            // 如果是加密数据，验证Base64格式；否则验证十六进制格式
            if (isEncrypted)
            {
                ValidateBase64String(hexPart, paramName);
            }
            else
            {
                ValidateHexFormat(hexPart, hexString, paramName);
            }
        }

        /// <summary>
        /// 验证Base64字符串格式
        /// </summary>
        /// <param name="base64String">Base64字符串</param>
        /// <param name="paramName">参数名</param>
        private static void ValidateBase64String(string base64String, string paramName)
        {
            try
            {
                // 尝试转换Base64字符串以验证格式
                Convert.FromBase64String(base64String);
            }
            catch (FormatException)
            {
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidBase64Format,
                    $"加密数据格式无效，期望Base64格式：{base64String}",
                    paramName,
                    "有效的Base64字符串",
                    base64String);
            }
        }

        /// <summary>
        /// 验证十六进制格式
        /// </summary>
        /// <param name="hexPart">十六进制数据部分</param>
        /// <param name="originalString">原始字符串（用于错误消息）</param>
        /// <param name="paramName">参数名</param>
        private static void ValidateHexFormat(string hexPart, string originalString, string paramName)
        {
            if (hexPart.Length % 2 != 0)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidHexFormat,
                    $"十六进制字符串长度必须为偶数，当前长度：{hexPart.Length}",
                    paramName,
                    "偶数长度",
                    $"{hexPart.Length}");
            
            // 验证字符是否都是有效的十六进制字符
            for (int i = 0; i < hexPart.Length; i++)
            {
                char c = hexPart[i];
                if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')))
                {
                    throw new DeviceMessageValidationException(
                        ValidationErrorType.InvalidHexFormat,
                        $"十六进制字符串包含无效字符 '{c}' 在位置 {i}：{originalString}",
                        paramName,
                        "有效的十六进制字符 (0-9, A-F, a-f)",
                        $"字符 '{c}' 在位置 {i}");
                }
            }
        }

        /// <summary>
        /// 验证密码
        /// </summary>
        /// <param name="password">密码</param>
        /// <param name="paramName">参数名</param>
        /// <exception cref="ArgumentNullException">密码为null时抛出</exception>
        /// <exception cref="DeviceMessageValidationException">密码为空字符串时抛出</exception>
        public static void ValidatePassword(string? password, string paramName = "password")
        {
            if (password == null)
                throw new ArgumentNullException(paramName, "密码不能为null");
                
            if (string.IsNullOrEmpty(password))
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidPassword,
                    "密码不能为空字符串，请传入null使用默认密码或传入有效密码",
                    paramName,
                    "null 或非空字符串",
                    "空字符串");
        }

        /// <summary>
        /// 验证设备数量限制
        /// </summary>
        /// <param name="deviceCount">设备数量</param>
        /// <exception cref="ArgumentOutOfRangeException">设备数量超过限制时抛出</exception>
        public static void ValidateDeviceCount(int deviceCount)
        {
            if (deviceCount > byte.MaxValue)
                throw new ArgumentOutOfRangeException(nameof(deviceCount), deviceCount, 
                    $"设备总数不能超过{byte.MaxValue}个（包括主设备和子设备），当前数量：{deviceCount}");
        }

        /// <summary>
        /// 验证读数数量限制
        /// </summary>
        /// <param name="readingCount">读数数量</param>
        /// <exception cref="ArgumentOutOfRangeException">读数数量超过限制时抛出</exception>
        public static void ValidateReadingCount(int readingCount)
        {
            if (readingCount > byte.MaxValue)
                throw new ArgumentOutOfRangeException(nameof(readingCount), readingCount, 
                    $"单个设备的读数数量不能超过{byte.MaxValue}个，当前数量：{readingCount}");
        }

        /// <summary>
        /// 验证状态数量限制
        /// </summary>
        /// <param name="stateCount">状态数量</param>
        /// <exception cref="ArgumentOutOfRangeException">状态数量超过限制时抛出</exception>
        public static void ValidateStateCount(int stateCount)
        {
            if (stateCount > byte.MaxValue)
                throw new ArgumentOutOfRangeException(nameof(stateCount), stateCount, 
                    $"单个读数的状态数量不能超过{byte.MaxValue}个，当前数量：{stateCount}");
        }

        /// <summary>
        /// 验证时间偏移
        /// </summary>
        /// <param name="timeOffset">时间偏移</param>
        /// <exception cref="ArgumentOutOfRangeException">时间偏移超出范围时抛出</exception>
        public static void ValidateTimeOffset(short timeOffset)
        {
            // 时间偏移可以是负数，但需要在合理范围内
            if (timeOffset < short.MinValue || timeOffset > short.MaxValue)
                throw new ArgumentOutOfRangeException(nameof(timeOffset), timeOffset, 
                    $"时间偏移超出有效范围 [{short.MinValue}, {short.MaxValue}]");
        }

        /// <summary>
        /// 验证主设备是否已配置
        /// </summary>
        /// <param name="hasMainDevice">是否有主设备</param>
        /// <exception cref="InvalidOperationException">未配置主设备时抛出</exception>
        public static void ValidateMainDeviceExists(bool hasMainDevice)
        {
            if (!hasMainDevice)
                throw new InvalidOperationException("主设备是必需的，请先调用 WithMainDevice 方法配置主设备");
        }

        /// <summary>
        /// 验证子设备添加前提条件
        /// </summary>
        /// <param name="hasMainDevice">是否有主设备</param>
        /// <exception cref="InvalidOperationException">添加子设备前未配置主设备时抛出</exception>
        public static void ValidateChildDevicePrerequisites(bool hasMainDevice)
        {
            if (!hasMainDevice)
                throw new InvalidOperationException("必须先设置主设备才能添加子设备，请先调用 WithMainDevice 方法");
        }

        /// <summary>
        /// 验证内存使用情况
        /// </summary>
        /// <param name="dataSize">数据大小（字节）</param>
        /// <param name="maxSize">最大允许大小（字节）</param>
        /// <exception cref="ArgumentOutOfRangeException">数据大小超过限制时抛出</exception>
        public static void ValidateMemoryUsage(long dataSize, long maxSize = 10 * 1024 * 1024) // 默认10MB限制
        {
            if (dataSize > maxSize)
                throw new ArgumentOutOfRangeException(nameof(dataSize), dataSize, 
                    $"数据大小超过内存限制 {maxSize / (1024 * 1024)}MB，当前大小：{dataSize / (1024 * 1024)}MB");
        }

        /// <summary>
        /// 验证加密/解密参数
        /// </summary>
        /// <param name="encryptFunc">加密函数</param>
        /// <param name="decryptFunc">解密函数</param>
        /// <param name="password">密码</param>
        /// <exception cref="ArgumentException">参数配置无效时抛出</exception>
        public static void ValidateEncryptionParameters(
            Func<string, string>? encryptFunc,
            Func<string, string>? decryptFunc,
            string? password)
        {
            // 如果提供了自定义加密函数，则必须同时提供解密函数
            if (encryptFunc != null && decryptFunc == null)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidEncryptionConfiguration,
                    "提供加密函数时必须同时提供解密函数",
                    nameof(decryptFunc),
                    "非null的解密函数",
                    "null");
            
            if (encryptFunc == null && decryptFunc != null)
                throw new DeviceMessageValidationException(
                    ValidationErrorType.InvalidEncryptionConfiguration,
                    "提供解密函数时必须同时提供加密函数",
                    nameof(encryptFunc),
                    "非null的加密函数",
                    "null");
            
            // 验证密码（如果提供）
            if (password != null)
                ValidatePassword(password, nameof(password));
        }

        /// <summary>
        /// 验证枚举值
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="value">枚举值</param>
        /// <param name="paramName">参数名</param>
        /// <exception cref="ArgumentOutOfRangeException">枚举值无效时抛出</exception>
        public static void ValidateEnum<T>(T value, string paramName = "value") where T : struct, Enum
        {
            if (!Enum.IsDefined(typeof(T), value))
                throw new ArgumentOutOfRangeException(paramName, value, $"无效的{typeof(T).Name}枚举值：{value}");
        }
    }
}